package core;

import gitobject.Commit;
import gitobject.Tree;
import repository.Repository;
import stage.Index;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.regex.Pattern;

public class JitCommit {
    /**
     * Recursively generate a list of relative paths of files from a tree
     * @param treeKey
     * @param objPathList
     * @throws IOException
     */
    private static void deserializeTree(String treeKey, ArrayList<String> objPathList) throws IOException {
        Tree tree = Tree.deserialize(treeKey);
        String[] objList = tree.getValue().split("\n");
        for (String obj: objList) {
            String[] objProps = obj.split(" ");
            if (objProps[1].equals("tree")){
                String subtreeKey = objProps[2];
//                System.out.println("deserialize "+objProps[3]);
                deserializeTree(subtreeKey, objPathList);
            }
            else { // blob
                if (new File(Repository.getWorkTree()+File.separator+objProps[3]).exists()) {
                    String blobInfo = objProps[2] + " " + objProps[3]; // key filepath
                    objPathList.add(blobInfo);
                }
            }
        }
    }

    /**
     * Generate a list of relative paths of files from last commit tree
     * @param lastCommitKey
     * @throws IOException
     */
    private static ArrayList<String> getLastCommitList(String lastCommitKey) throws IOException {
        ArrayList<String> objPathList = new ArrayList<String>();
        // get commit tree
        Commit lastCommit = Commit.deserialize(lastCommitKey);
        String treeKey = lastCommit.getTree();
        deserializeTree(treeKey, objPathList);

        return objPathList;
    }


    /**
     * Generate a list of relative paths of files from index file
     * @return ArrayList<String>
     * @throws IOException
     */
    private static ArrayList<String> getCurrentIndexList() throws IOException {
        ArrayList<String> blobPathList = new ArrayList<String>();

        LinkedList<String[]> indexList = new Index().getIndexList();
        for(String[] arr : indexList) {
            if (new File(Repository.getWorkTree()+File.separator+arr[1]).exists()) {
                String blobInfo = arr[0] + " " + arr[1]; // key filepath
                blobPathList.add(blobInfo);
            }
        }
        return blobPathList;
    }

    /**
     * Build commit tree from current stage and parent commit tree (if exists)
     * @return Tree
     * @throws Exception
     */
    public static Tree buildCommitTree() throws Exception {
        Commit commit = new Commit();
        String lastCommitKey = commit.getLastCommit();

        ArrayList<String> currentIndexList = getCurrentIndexList();
        if (lastCommitKey != null) {
            ArrayList<String> lastCommitList = getLastCommitList(lastCommitKey);
            currentIndexList.addAll(lastCommitList); // merge two lists
        }

        // define comparator (sort by depth of file,
        Comparator<String> pathComparator = new Comparator<String>() {
            @Override public int compare(String s1, String s2) {
                String pattern = Pattern.quote(System.getProperty("file.separator"));
                Integer len1 = s1.split(pattern).length;
                Integer len2 = s2.split(pattern).length;
                if (len1 > len2) return -1;
                if (len1 < len2) return 1;
                return compareFragments(s1, s2);
            }
                public int compareFragments(String s1, String s2) {
                    String pattern = Pattern.quote(System.getProperty("file.separator"));
                    Integer len1 = s1.split(pattern).length;
                    Integer len2 = s2.split(pattern).length;
                    for (int i=0; i<len1; i++) {
                        if (s1.split(pattern)[i].compareTo(s2.split(pattern)[i])>0) {
                            return 1;
                        }
                        if (s1.split(pattern)[i].compareTo(s2.split(pattern)[i])<0) {
                            return -1;
                        }
                    }
                    return 0;
                }
        };
        // use TreeMap to store tree path and tree key in bottom-up order
        Map<String, String> treeMap = new TreeMap<String, String>(pathComparator);
        for (String blobInfo:currentIndexList) {
            String path = blobInfo.split(" ")[1];
            if (path.contains(File.separator)) { // 非顶层文件
                String dirPath = path.substring(0, path.lastIndexOf(File.separator)); // 取相对路径至最底层文件夹
                String pattern = Pattern.quote(System.getProperty("file.separator"));
                String[] paths = dirPath.split(pattern);
                String concatPath = paths[0];
                for (int i=0; i<paths.length; i++) {
                    if (i>=1) concatPath += (File.separator+paths[i]);
                    treeMap.put(concatPath, "");
                }
            }
        }

        // generate all subdir tree key
        for (Map.Entry<String, String> entry : treeMap.entrySet()) {
            String dir = entry.getKey();

            Tree tree = new Tree(dir, currentIndexList, treeMap);
            String treeKey = tree.getKey();
//            treeMap.remove(dir);
            treeMap.replace(dir, treeKey); // update tree key
        }

        Tree root = new Tree(currentIndexList, treeMap); // 构造commit tree
        return root;
    }




    /**
     * Generate commit object to ./object
     * @param commitMsg
     * @throws Exception
     */
    public static void commit(String commitMsg) throws Exception {
        Tree t = buildCommitTree();

        String author = "group7";
        String committer = "group7";
        Commit commit = new Commit(t, author, committer, commitMsg);

        File HEAD = new File(Repository.getGitDir() + File.separator + "HEAD");
        String path = commit.getValue(HEAD).substring(5).replace("\n", "");
        File branchFile = new File(Repository.getGitDir() + File.separator + path);
        branchFile.createNewFile();
        FileWriter fw = new FileWriter(branchFile, false);
        fw.write(commit.getKey());
        fw.close();

        File indexFile = new File(Repository.getGitDir() + File.separator + "index");
        new FileWriter(indexFile, false).close();

        String currentHead = JitBranch.getHEAD();
        System.out.println("[" + currentHead + " " + commit.getKey().substring(0,6) + "]" + commit.getMessage()); // commit return massage
    }
}
